#pragma once

#include <unordered_map>
#include <functional>
#include <iostream>
#include <string>
#include "Protocol.hpp"
#include "Epoller.hpp"
#include "Sock.hpp"
#include "Util.hpp"

using namespace protocol_ns;
class Connection;

const static uint16_t default_port = 8888;
const static int bsize = 1024;
using func_t = std::function<Response(Request&)>;
using callback_t = std::function<void(Connection *)>;

class Connection
{
public:
    Connection(int fd)
        : _fd(fd)
    {
    }

    void Register(callback_t recv, callback_t send, callback_t excpet)
    {
        _recever = recv;
        _sender = send;
        _excepter = excpet;
    }

    ~Connection()
    {
    }

public:
    int _fd;
    // 为每一个fd创建用户级缓冲区
    std::string inbuffer;
    std::string outbuffer;

    callback_t _recever;
    callback_t _sender;
    callback_t _excepter;
};

class EpollServer
{
    const static int N = 64;

public:
    EpollServer(func_t func, uint16_t port = default_port)
        : _port(port), _func(func)
    {
    }

    void InitServer()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        AddConnection(_listensock.GetFd());

        ep.Create();
        // 设置ET模式
        ep.CtlEvent(_listensock.GetFd(), EPOLL_CTL_ADD, EPOLLIN | EPOLLET);
    }

    void Start()
    {
        LogMessage(Info, "Ready to provide service...");
        int timeout = -1;
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    void LoopOnce(int timeout)
    {
        int n = ep.Wait(_recvs, N, timeout);
        for (int i = 0; i < n; i++)
        {
            int fd = _recvs->data.fd;
            // 将异常事件交由读写内部处理
            if((_recvs[i].events & EPOLLERR) || (_recvs[i].events & EPOLLHUP))
                _recvs[i].events |= (EPOLLIN | EPOLLOUT);

            if ((_recvs[i].events & EPOLLIN) && IsConnectionExist(fd))
                connections[fd]->_recever(connections[fd]);

            if ((_recvs[i].events & EPOLLOUT) && IsConnectionExist(fd))
                connections[fd]->_sender(connections[fd]);
        }
    }

    void AddConnection(int fd)
    {
        Connection *conn = new Connection(fd);
        // 为每一个对象绑定回调函数
        // 弱化listensock对象的概念
        if (fd == _listensock.GetFd())
        {
            conn->Register(std::bind(&EpollServer::Accept, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn->Register(std::bind(&EpollServer::Recv, this, std::placeholders::_1)
                         , std::bind(&EpollServer::Send, this, std::placeholders::_1)
                         , std::bind(&EpollServer::Except, this, std::placeholders::_1));
        }

        connections[fd] = conn;
        ep.CtlEvent(fd, EPOLL_CTL_ADD, EPOLLIN | EPOLLET);
    }
    // 防止失效fd再参与读写过程
    bool IsConnectionExist(int fd)
    {
        return connections.find(fd) != connections.end();
    }

    void EnableReadWrite(int fd, bool ifread, bool ifwrite)
    {
        uint32_t event = (ifread ? EPOLLIN: 0) | (ifwrite ? EPOLLOUT: 0) | EPOLLET;
        ep.CtlEvent(fd, EPOLL_CTL_MOD, event);
    }

    void Accept(Connection *conn)
    {
        do
        {
            // 注意一下是否真的需要err
            int err;
            uint16_t client_port;
            std::string client_ip;
            int sock = _listensock.Accept(&client_ip, &client_port, &err);

            if (sock > 0)
            {
                AddConnection(sock);
                LogMessage(Info, "Accept success...");
            }
            else
            {
                if (err == EAGAIN || err == EWOULDBLOCK) // 读到缓冲区为空
                    break;
                else if (err == EINTR) // 因为信号中断
                    continue;
                else
                {
                    LogMessage(Warring, "Accept error, errno: %d, err_string: %s", err, strerror(err));
                    continue;
                }
            }
        } while (_recvs[conn->_fd].events & EPOLLET);
    }

    void Recv(Connection *conn)
    {
        // 现将接收缓冲区中的数据读完
        int fd = conn->_fd;
        do
        {
            char buffer[bsize];
            int n = recv(fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                connections[fd]->inbuffer += buffer;
            }
            else if (n == 0) // 对方关闭连接
            {
                conn->_excepter(conn);
                return; // 必须是return, 不能是break，因为如果连接已经关闭，后续的操作全都是越界
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->_excepter(conn);
                    continue;
                }
            }
        } while (_recvs[fd].events & EPOLLET);
        // 数据处理
        RequestHandler(conn);
        if(!conn->outbuffer.empty())
            conn->_sender(conn);
    }

    void Send(Connection *conn)
    {
        // 打开读写功能 存在对方缓冲区被写满了而退出的情况
        int fd = conn->_fd;
        do
        {
            int n = send(fd, conn->outbuffer.c_str(), conn->outbuffer.size(), 0);
            if(n > 0)
            {
                conn->outbuffer.erase(0, n);
                if(!conn->outbuffer.empty())
                    break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    conn->_excepter(conn);
                    continue;
                }
            }
        } while (_recvs[fd].events & EPOLLET);
        
        if(!conn->outbuffer.empty()) // 因为对方缓冲区被打满，而退出，需要重新设置
            EnableReadWrite(fd, true, true);
        else
            EnableReadWrite(fd, true, false);
    }

    void Except(Connection *conn)
    {
        ep.DelEvent(conn->_fd); // Epoll中删除的fd必须是合法的
        close(conn->_fd);
        connections.erase(conn->_fd);
        delete conn;

        LogMessage(Info, "Delete client success...");
    }

    void RequestHandler(Connection *conn)
    {
        int quit = false;
        while(!quit)
        {
            std::string reqStr;
            int n = ReadPackage(conn->inbuffer, &reqStr);
            if(n > 0)
            {
                reqStr = RemoveHeader(reqStr, n); // 去掉报头
                Request req;
                req.Deserialize(reqStr); // 反序列化

                Response resp = _func(req); // 业务处理

                std::string respStr;
                resp.Serialize(&respStr); // 序列化
                respStr = AddHeader(respStr); // 添加报头

                conn->outbuffer += respStr; // 放入发送缓冲区
            }
            else
                quit = true;
        }
    }

    ~EpollServer()
    {
    }

private:
    epoller ep;
    func_t _func;
    uint16_t _port;
    Sock _listensock;
    struct epoll_event _recvs[N];
    std::unordered_map<int, Connection *> connections;
};